#include "npapi.h"
#include "npfunctions.h"
#include "npruntime.h"

#include "scriptable_object.h"
#include "common.h"
#include "scriptable_methods.h"
#include <fstream>
//#include "OWSNewDocument3Proxy.h"

extern NPNetscapeFuncs* browserFuncs;

NPObject* ScrNPObjAllocate(NPP npp, NPClass *aClass)
{
	TRACE("NPAllocate");
	ScriptableObject* obj = new ScriptableObject;
	obj->NPObj.referenceCount = 0;
	obj->instance = npp;
	obj->comObj = 0;
	return (NPObject*)obj;
}

void ScrNPObjDeallocate(NPObject *npobj)
{
	TRACE("ScrNPObjDeallocate");
	ScriptableObject* obj = (ScriptableObject*)npobj;
	if(obj->comObj)
	{
		ULONG comObjRecCount = obj->comObj->Release();
		TRACE("OWS obj ref count (after release): %u", comObjRecCount);
	}
	delete obj;
}

void ScrNPObjInvalidate(NPObject *npobj)
{
	TRACE("ScrNPObjInvalidate");
}

bool ScrNPObjHasMethod(NPObject *npobj, NPIdentifier name)
{
	const char* methodName = browserFuncs->utf8fromidentifier(name);
	TRACE("ScrNPObjHasMethod %s", methodName);
	ScriptableMethod method = GetScriptableMethod(methodName);
	return method != 0;
}

bool ScrNPObjInvoke(NPObject *npobj, NPIdentifier name,
                                    const NPVariant *args, uint32_t argCount,
                                    NPVariant *result)
{
	const char* methodName = browserFuncs->utf8fromidentifier(name);
	TRACE("ScrNPObjInvoke %s", methodName);
	ScriptableMethod method = GetScriptableMethod(methodName);
	if(method == 0)
		return false;
	ScriptableObject* scrObj = (ScriptableObject*)npobj;
	bool res = method(scrObj, browserFuncs, argCount, args);
	result->type = NPVariantType_Bool;
	result->value.boolValue = res;
	return res;
}

bool ScrNPObjInvokeDefault(NPObject *npobj,
                                           const NPVariant *args,
                                           uint32_t argCount,
                                           NPVariant *result)
{
	TRACE("ScrNPObjInvokeDefault");
	return false;
}

bool ScrNPObjHasProperty(NPObject *npobj, NPIdentifier name)
{
	const char* methodName = browserFuncs->utf8fromidentifier(name);
	TRACE("ScrNPObjHasProperty %s", methodName);
	return false;
}

bool ScrNPObjGetProperty(NPObject *npobj, NPIdentifier name,
                                         NPVariant *result)
{
	TRACE("ScrNPObjGetProperty");
	return false;
}

bool ScrNPObjSetProperty(NPObject *npobj, NPIdentifier name,
                                         const NPVariant *value)
{
	TRACE("ScrNPObjSetProperty");
	return false;
}

bool ScrNPObjRemoveProperty(NPObject *npobj,
                                            NPIdentifier name)
{
	TRACE("ScrNPObjRemoveProperty");
	return false;
}

bool ScrNPObjEnumeration(NPObject *npobj, NPIdentifier **value,
                                         uint32_t *count)
{
	TRACE("ScrNPObjEnumeration");
	return false;
}

bool ScrNPObjConstruct(NPObject *npobj,
                                       const NPVariant *args,
                                       uint32_t argCount,
                                       NPVariant *result)
{
	TRACE("ScrNPObjConstruct");
	return false;
}

NPClass ScriptableObjectClass =
{
	NP_CLASS_STRUCT_VERSION,
	ScrNPObjAllocate,
	ScrNPObjDeallocate,
	ScrNPObjInvalidate,
	ScrNPObjHasMethod,
	ScrNPObjInvoke,
	ScrNPObjInvokeDefault,
	ScrNPObjHasProperty,
	ScrNPObjGetProperty,
	ScrNPObjSetProperty,
	ScrNPObjRemoveProperty,
	ScrNPObjEnumeration,
	ScrNPObjConstruct
};

NPObject* CreateScriptableObject(NPP instance)
{
	TRACE("Entered CreateScriptableObject");
	NPObject* obj = browserFuncs->createobject(instance, &ScriptableObjectClass);
	TRACE("Exiting CreateScriptableObject");
	return obj;
}

TCHAR *find_filename(TCHAR *filePath)
{
	TCHAR *result = wcsrchr(filePath, '\\');
    return result != 0? result + 1: filePath;
}

TCHAR *find_filepath(TCHAR *path, TCHAR *filePath)
{
	size_t size = find_filename(filePath) - filePath;
	wcsncpy(path, filePath, size);
	path[size] = 0;
    return path;
}

HRESULT CoLoadInstance(REFCLSID rclsid, IUnknown* pUnkOuter,
  REFIID riid, LPVOID FAR* ppv)
{
	LPOLESTR lpClsid;
	HRESULT hr = StringFromCLSID(rclsid, &lpClsid);
	if (S_OK != hr)
	{
		return hr;
	}

	TCHAR path[256];
	wsprintf(path, L"\\CLSID\\%s\\InprocServer32", lpClsid);

	TCHAR data[256];
	LONG cbData = 256;
	LONG res;

	if (ERROR_SUCCESS != (res = RegQueryValueW(HKEY_CLASSES_ROOT, path, data, &cbData)))
	{
		return hr;
	}
  
	TCHAR dllPath[256];
	LPCTSTR szDllPath = find_filepath(dllPath, data);
	LPCTSTR szDllName = find_filename(data);

	hr = REGDB_E_KEYMISSING;
	if (!SetDllDirectoryW(szDllPath))
	{
		return hr;
	}
	HMODULE hDll = LoadLibraryW(szDllName);
	if (hDll == 0)
	return hr;

	typedef HRESULT (__stdcall *pDllGetClassObject)(IN REFCLSID rclsid, 
					IN REFIID riid, OUT LPVOID FAR* ppv);

	pDllGetClassObject GetClassObject = 
		(pDllGetClassObject)::GetProcAddress(hDll, "DllGetClassObject");
	if (GetClassObject == 0)
	{
		::FreeLibrary(hDll);
		return hr;
	}

	IClassFactory *pIFactory;

	hr = GetClassObject(rclsid, IID_IClassFactory, (LPVOID *)&pIFactory);

	if (!SUCCEEDED(hr))
		return hr;

	hr = pIFactory->CreateInstance(pUnkOuter, riid, ppv);
	pIFactory->Release();

	return hr;
}